package com.book.oauth.config;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.book.oauth.exception.CustomAccessDeniedHandler;
import com.book.oauth.exception.CustomAuthExceptionEntryPoint;
import com.book.oauth.server.client.entity.Client;
import com.book.oauth.server.client.service.IClientService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;

/**
 * Description： 给接口地址让security管理起来，如哪些不需要授权能访问，
 * 哪些需要登录授权后能访问，哪些需要用户拥有这些角色才能访问。
 *
 * @Author： leo.xiong
 * @CreateDate： 2019/10/19 14:59
 * @Email： leo.xiong@suyun360.com
 * Version：1.0
 */
@Slf4j
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Value("${custom.clientId}")
    private String clientId;

    @Value("${adminPath:}")
    protected String adminPath;

    @Autowired
    private IClientService clientService;
    /**
     * Autho2的角色前缀
     */
    public static final String ROLE_PREFIX = "ROLE_";
    /**
     * 测试角色
     */
    public static final String TEST = "TEST";
    /**
     * 资源角色
     */
    public static final String RESOURCE = "RESOURCE";
    /**
     * 管理员角色
     */
    public static final String ADMIN = "ADMIN";
    /**
     * 普通用户角色
     */
    public static final String USER = "USER";

    /**
     * 路径匹配规则
     * 优先级：1、匹配静态>2、匹配oauth/token等oauth2自己管理的路径>3、ResourceServerConfig需要有某某角色的资源信息>4、WebSecurityConfig路径规则
     * FilterChainProxy,WebSecurity
     *
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/swagger-ui.html/**").permitAll()
                .antMatchers("/swagger-resources/**").permitAll()
                .antMatchers("/v2/**").permitAll()
                .antMatchers("/csrf/**").permitAll()
                .antMatchers("/favicon.ico/**").permitAll()
                .antMatchers("/webjars/**").permitAll()
                .antMatchers("/actuator/**").permitAll()
                .antMatchers("/hystrix.stream/**").permitAll()
                .antMatchers("/" + adminPath + "/registered").permitAll()
                .antMatchers("/" + adminPath + "/test/**").hasRole(TEST)
                .antMatchers("/" + adminPath + "/resource/**").hasRole(RESOURCE)
                .antMatchers("/" + adminPath + "/user/**").hasRole(USER)
                .antMatchers("/" + adminPath + "/**").hasRole(ADMIN)
                .anyRequest().denyAll();
    }

    /**
     * 根据client_id设置资源id
     * 设置自定义授权异常信息
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        Client client = clientService.getClientByCacheAndDb(clientId);
        if (client == null) {
            log.error("clientId:{}不存在", clientId);
        } else {
            String resource = client.getResourceIds();
            if (StringUtils.isEmpty(resource)) {
                log.error("clientId:{}资源未配置", clientId);
            } else {
                //设置客户端所能访问的资源id集合(默认取第一个是本服务的资源)
                resources.resourceId(resource.split(StringPool.COMMA)[0]).stateless(true);
            }
        }
        //自定义Token异常信息,用于token校验失败返回信息
        resources.authenticationEntryPoint(new CustomAuthExceptionEntryPoint())
                //授权异常处理
                .accessDeniedHandler(new CustomAccessDeniedHandler());
    }

}

